package com.moss.cloud.common.security.config;

import com.alibaba.fastjson2.JSONObject;
import com.moss.cloud.common.core.constant.Constant;
import com.moss.cloud.common.core.custom.CustomHttpHeaders;
import com.moss.cloud.common.core.enums.GrantTypeEnum;
import com.moss.cloud.common.core.model.from.AuthFrom;
import com.moss.cloud.common.security.component.RedisTemplateOps;
import com.moss.cloud.common.security.condition.CustomCondition;
import com.moss.cloud.common.security.permission.AuthRpcService;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 自定义Feign请求的客户端认证
 *
 * @author 瑾年
 * @date 2023年11月15日
 */
@Slf4j
@Configuration
@EnableFeignClients
@ConditionalOnClass(WebApplicationContext.class)
@ConditionalOnMissingBean(WebApplicationContext.class)
@Conditional(CustomCondition.class)
public class OAuth2ClientAuthenticationConfig {
    @Value("${moss.cloud.clientId}")
    private String clientId;
    @Value("${moss.cloud.clientSecret}")
    private String clientSecret;
    private final AuthRpcService authRpcService;
    private final RedisTemplateOps redisTemplateOps;

    public OAuth2ClientAuthenticationConfig(AuthRpcService authRpcService, RedisTemplateOps redisTemplateOps) {
        this.authRpcService = authRpcService;
        this.redisTemplateOps = redisTemplateOps;
    }

    @Bean
    public TokenAuthRequestInterceptor tokenAuthRequestInterceptor() {
        Boolean hasKey = redisTemplateOps.hasKey(Constant.CLIENT_TOKEN + clientId);
        String headerValue;
        if (hasKey.equals(Boolean.TRUE)) {
            log.info("redis中存在客户端认证信息:{}", clientId);
            headerValue = (String) redisTemplateOps.get(Constant.CLIENT_TOKEN + clientId);
        } else {
            log.info("redis中不存在客户端认证信息重新认证客户端:{}", clientId);
            String authorization = GrantTypeEnum.DEF_AUTHORIZATION.getValue();
            AuthFrom authFrom = GrantTypeEnum.authFrom(clientId, clientSecret);
            JSONObject json = authRpcService.token(authorization, CustomHttpHeaders.CONTENT_TYPE, CustomHttpHeaders.CACHE_CONTROL, authFrom);
            headerValue = json.getString(GrantTypeEnum.ACCESS_TOKEN.getValue());
            Long expires = json.getLong(GrantTypeEnum.EXPIRES_IN.getValue());
            redisTemplateOps.set(Constant.CLIENT_TOKEN + clientId, headerValue, expires);
        }
        return new TokenAuthRequestInterceptor(headerValue);
    }


    public static class TokenAuthRequestInterceptor implements RequestInterceptor {
        private final String headerValue;

        public TokenAuthRequestInterceptor(String headerValue) {
            this.headerValue = headerValue;
        }

        @Override
        public void apply(RequestTemplate template) {
            log.debug("Adding token to request: {}", headerValue);
            template.header(CustomHttpHeaders.FROM_INNER, GrantTypeEnum.TOKEN_TYPE.getValue() + headerValue);
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = extractTokenFromRequest(request);
            if (StringUtils.isNotBlank(token)) {
                template.header(HttpHeaders.AUTHORIZATION, token);
            }
        }

        private String extractTokenFromRequest(HttpServletRequest request) {
            return request.getHeader(HttpHeaders.AUTHORIZATION);
        }
    }

}
